/*
 *  levelUpGame - ViewController.cpp
 *  Copyright 2011 W. Reckman. All rights reserved.
 */
#include <algorithm>
#include "Settings.h"
#include "GameModel.h"
#include "LevelUpApp.h"
#include "ViewController.h"

//--------------------------------------------------------------
ViewController::ViewController( const LevelUpApp& appRef_ )
: s( Settings::getInstance() ), game( appRef_.getGame() ), renderToBeamer( false ),
  viewportWidth( ofGetWidth() ), viewportHeight( ofGetHeight() ),
  bgView( appRef_ ), gameView( appRef_ )
{
	videoState.minWin = 0.0f;
	videoState.maxWin = 1.0f;
	videoState.backgroundSpeed = videoState.facadeSpeed = 1;
}

//--------------------------------------------------------------
void ViewController::setup() {
	/* init game font */
	
	string gameFontName = *s.getValueInGroup( "game_font_file", "resource", "fonts/monaco.ttf" );
	string pixelFontName = *s.getValueInGroup( "pixel_font_file", "resource", "fonts/Fixedsys500c.ttf" );
	
	int smallFontSize = ofToInt( *s.getValueInGroup( "game_small_font_size", "resource", "36" ) );
	int mediumFontSize = ofToInt( *s.getValueInGroup( "game_medium_font_size", "resource", "108" ) );
	int largeFontSize = ofToInt( *s.getValueInGroup( "game_large_font_size", "resource", "288" ) );
	int smallPixelFontSize = ofToInt( *s.getValueInGroup( "pixel_small_font_size", "resource", "24" ) );
	int largePixelFontSize = ofToInt( *s.getValueInGroup( "pixel_large_font_size", "resource", "108" ) );
	
	string balPtrFile = *s.getValueInGroup( "iface_balance_pointer", "resource", "balance_pointer" );
	string balTgtPrefix = *s.getValueInGroup( "iface_balance_target_prefix", "resource", "balance_target" );
	string balTgtSuffix = *s.getValueInGroup( "iface_balance_target_suffix", "resource", ".png" );
	
	bgVideoFollowsBalance = parseBool( *s.getValueInGroup( "bg_video_follows_balance", "resource", "false" ) );
	bgVideoHasMirrorCopy = parseBool( *s.getValueInGroup( "bg_video_has_mirror_copy", "resource", "false" ) );
	facadeVideoFollowsBalance = parseBool( *s.getValueInGroup( "facade_video_follows_balance", "resource", "false" ) );
	facadeVideoHasMirrorCopy = parseBool( *s.getValueInGroup( "facade_video_has_mirror_copy", "resource", "false" ) );
	
	smallFont.loadFont( gameFontName, smallFontSize );
	mediumFont.loadFont( gameFontName, mediumFontSize );
	largeFont.loadFont( gameFontName, largeFontSize );
	smallPixelFont.loadFont( pixelFontName, smallPixelFontSize );
	largePixelFont.loadFont( pixelFontName, largePixelFontSize );
	
	/* init canvas dimensions */
	
	canvasWidth = ofToInt( *s.getValueInGroup( "canvas_width", "settings", "-1" ) );
	canvasHeight = ofToInt( *s.getValueInGroup( "canvas_height", "settings", "-1" ) );
	
	videoWindowSize = ofToFloat( *s.getValueInGroup( "video_pingpong_window", "game", "0.1" ) );
	
	if ( canvasWidth == -1 || canvasHeight == -1 ) {
		ofLog( OF_LOG_ERROR, "(ViewController.cpp) missing config key canvas_width or canvas_height" );
		//FIXME: FATAL_ERROR
	} else {
		ofLog( OF_LOG_WARNING, "(ViewController) using %ix%i as canvas texture resolution", canvasWidth, canvasHeight );
		
		fboTexture.allocate( canvasWidth, canvasHeight );
		
		bgView.setup();
		gameView.setup();
	}
	
	/* init balance target images */
	
	for ( int i = 0; i < game.getMaxNumPlayers(); ++i ) {
		tgtBalanceImages.push_back( ofImage() );
		const string filename = balTgtPrefix + ofToString( i + 1 ) + balTgtSuffix;
		bool r = tgtBalanceImages[i].loadImage( filename );
		if ( ! r ) {
			ofLog( OF_LOG_ERROR, "(ViewController.cpp) error loading balance target image '%s'", filename.c_str() );
		}
	}
	
	bool r = balancePointerImage.loadImage( balPtrFile );
	if ( ! r ) {
		ofLog( OF_LOG_ERROR, "(ViewController.cpp) error loading balance pointer image '%s'", balPtrFile.c_str() );
	}
	
	/* init feedback text images */
	r = initFeedbackImages();
	if ( ! r ) {
		ofLog( OF_LOG_ERROR, "(ViewController.cpp) could not load one or more feedback text images" );
	}
}

//--------------------------------------------------------------
void ViewController::update() {
	float balanceNorm = game.getBalanceMeter().getBalanceFloat() + 0.5f;
	float bgPos = bgView.getBackgroundPosition(), fcPos = bgView.getFacadePosition();
	float prevBgSpeed = videoState.backgroundSpeed, prevFcSpeed = videoState.facadeSpeed;
	videoState.minWin = max( 0.0f, balanceNorm - videoWindowSize / 2.0f );
	videoState.maxWin = min( 1.0f, balanceNorm + videoWindowSize / 2.0f );
	
	if ( bgVideoFollowsBalance && bgVideoHasMirrorCopy ) {
		if ( videoState.backgroundSpeed < 0 ) bgPos = 1.0f - bgPos;
		bgPos *= 2.0f;
	}
	
	if ( bgVideoFollowsBalance && bgVideoHasMirrorCopy ) {
		if ( videoState.facadeSpeed < 0 ) fcPos = 1.0f - fcPos;
		fcPos *= 2.0f;
	}
	
	if ( bgPos <= videoState.minWin ) videoState.backgroundSpeed = 1;
	else if ( bgPos >= videoState.maxWin ) videoState.backgroundSpeed = -1;
	
	if ( fcPos <= videoState.minWin ) videoState.facadeSpeed = 1;
	else if ( fcPos >= videoState.maxWin ) videoState.facadeSpeed = -1;
	
//TEMP
if ( prevBgSpeed != videoState.backgroundSpeed || prevFcSpeed != videoState.facadeSpeed ) {
	ofLog( OF_LOG_WARNING, "(ViewController.cpp) a dir changed[%.1f]: bg(%.1f) %1.0f -> %1.0f; fc(%.1f) %1.0f -> %1.0f",
				balanceNorm, bgPos, prevBgSpeed, videoState.backgroundSpeed, fcPos, prevFcSpeed, videoState.facadeSpeed );
}
	
	if ( bgVideoFollowsBalance && prevBgSpeed != videoState.backgroundSpeed ) {
		if ( ! bgVideoHasMirrorCopy ) {
			bgView.setBackgroundSpeed( videoState.backgroundSpeed );
		} else {
			float newRealPos = ( videoState.backgroundSpeed > 0 ) ? bgPos / 2.0f : 1.0f - bgPos / 2.0f;
			bgView.setBackgroundPosition( newRealPos );
		}
	}
	
	if ( facadeVideoFollowsBalance && prevFcSpeed != videoState.facadeSpeed ) {
		if ( ! facadeVideoHasMirrorCopy ) {
			bgView.setFacadeSpeed( videoState.facadeSpeed );
		} else {
			float newRealPos = ( videoState.facadeSpeed > 0 ) ? fcPos / 2.0f : 1.0f - fcPos / 2.0f;
			bgView.setFacadePosition( newRealPos );
		}
	}
	
	bgView.update();
	gameView.update();
}

//--------------------------------------------------------------
void ViewController::draw() {
	
	ofBackground(0, 0, 0);
	
	fboTexture.begin();
	bgView.draw();
	gameView.draw();
	fboTexture.end();
	
	fboTexture.draw( 0, 0, viewportWidth, viewportHeight );
}

//--------------------------------------------------------------
const ofImage* ViewController::getFeedbackImage( int playerId_, FEEDBACK_IMAGES fbType ) const {
	if ( playerId_ < 0 || playerId_ >= game.getMaxNumPlayers() ) return 0;
	
	const vec_ofImage& playerImgs = feedbackImages[playerId_];
	return &playerImgs[fbType];
}

//--------------------------------------------------------------
ofTrueTypeFont& ViewController::getSmallFont() const { return smallFont; }
ofTrueTypeFont& ViewController::getMediumFont() const { return mediumFont; }
ofTrueTypeFont& ViewController::getLargeFont() const { return largeFont; }
ofTrueTypeFont& ViewController::getSmallPixelFont() const { return smallPixelFont; }
ofTrueTypeFont& ViewController::getLargePixelFont() const { return largePixelFont; }

//--------------------------------------------------------------
int ViewController::getCanvasWidth() const { return canvasWidth; }
int ViewController::getCanvasHeight() const { return canvasHeight; }

//--------------------------------------------------------------
const ViewController::VideoState& ViewController::getVideoState() const {
	return videoState;
}

//--------------------------------------------------------------
const ofImage& ViewController::getBalancePointerImage() const { return balancePointerImage; }
const ViewController::vec_ofImage& ViewController::getTgtBalanceImages() const { return tgtBalanceImages; }

//--------------------------------------------------------------
//FIXME: is this function called with incorrect parameters? (fullscreen resolution is wrong)
void ViewController::setViewportSize( int w, int h ) {
#ifdef FORCE_VIEWPORT_4_TO_3
	float ft = 3.0f / 4.0f, tf = 4.0f / 3.0f;
	if ( w * ft > h ) w = h * tf;
	else if ( h * tf > w ) h = w * ft;
#endif
	
	viewportWidth = w;
	viewportHeight = h;
	
	ofLog( OF_LOG_NOTICE, "(ViewController.cpp) viewport resolution set to %i x %i", viewportWidth, viewportHeight );
}


/******************
 * PRIVATE IMAGES *
 ******************/

//FIXME: relies on the value of enum, dangerous!
bool ViewController::initFeedbackImages() {
	bool result = true;
	static const string colors[3] = { "Green", "Purple", "Orange" };
	string fbPrefix = *s.getValueInGroup( "iface_feedback_text_prefix", "resource", "FeedbackText" );
	string fbBadSuffix = *s.getValueInGroup( "iface_feedback_text_bad_suffix", "resource", "FeedbackText" );
	string fbModerateSuffix = *s.getValueInGroup( "iface_feedback_text_mid_suffix", "resource", "FeedbackText" );
	string fbGoodSuffix = *s.getValueInGroup( "iface_feedback_text_good_suffix", "resource", "FeedbackText" );
	string fbBalanceSuffix = *s.getValueInGroup( "iface_feedback_text_bal_suffix", "resource", "FeedbackText" );
	
	feedbackImages.clear();
	for ( int i = 0; i < sizeof( colors ) / sizeof( colors[0] ); ++i ) {
		bool lr;
		string s;
		const string& cs = colors[i];
		feedbackImages.push_back( vec_ofImage() );
		vec_ofImage& voi = feedbackImages[i];
		voi.push_back( ofImage() ); voi.push_back( ofImage() ); voi.push_back( ofImage() ); voi.push_back( ofImage() );
		
		s = fbPrefix + colors[i] + fbBadSuffix;
		if ( ! voi[FB_BAD].loadImage( s ) ) {
			ofLog( OF_LOG_ERROR, "(ViewController.cpp) could not load feedback image '%s'", s.c_str() );
			result = false;
		}
		
		s = fbPrefix + colors[i] + fbModerateSuffix;
		if ( ! voi[FB_MODERATE].loadImage( s ) ) {
			ofLog( OF_LOG_ERROR, "(ViewController.cpp) could not load feedback image '%s'", s.c_str() );
			result = false;
		}
		
		s = fbPrefix + colors[i] + fbGoodSuffix;
		if ( ! voi[FB_GOOD].loadImage( s ) ) {
			ofLog( OF_LOG_ERROR, "(ViewController.cpp) could not load feedback image '%s'", s.c_str() );
			result = false;
		}
		
		s = fbPrefix + colors[i] + fbBalanceSuffix;
		if ( ! voi[FB_BALANCE].loadImage( s ) ) {
			ofLog( OF_LOG_ERROR, "(ViewController.cpp) could not load feedback image '%s'", s.c_str() );
			result = false;
		}
	}
	
	return result;
}
